package com.evancharlton.mileage.provider.tables;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Set;

import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;

import com.evancharlton.mileage.dao.Dao;
import com.evancharlton.mileage.dao.Dao.Column;

public abstract class ContentTable {
	protected static String TABLE_NAME = "content_table";

	abstract public String getTableName();

	public static final HashMap<String, String> buildProjectionMap(String[] map) {
		HashMap<String, String> projection = new HashMap<String, String>();
		// just in case
		projection.put(BaseColumns._ID, BaseColumns._ID);
		for (String key : map) {
			projection.put(key, key);
		}
		return projection;
	}

	public String getDefaultSortOrder() {
		return BaseColumns._ID + " desc";
	}

	abstract public void registerUris();

	public int delete(SQLiteDatabase db, Uri uri, String selection, String[] selectionArgs) {
		try {
			long id = ContentUris.parseId(uri);
			if (selection == null) {
				selection = "";
			}
			selection += BaseColumns._ID + " = ?";

			if (selectionArgs == null) {
				selectionArgs = new String[0];
			}
			final int length = selectionArgs.length + 1;
			String[] args = new String[length];
			for (int i = 0; i < length - 1; i++) {
				args[i] = selectionArgs[i];
			}
			args[length - 1] = String.valueOf(id);
			selectionArgs = args;
		} catch (UnsupportedOperationException e) {
			// silently fail
		} catch (NumberFormatException e) {
			// silently fail
		}
		return db.delete(getTableName(), selection, selectionArgs);
	}

	public final boolean isValidType(int type) {
		return getType(type) != null;
	}

	abstract public String getType(int type);

	abstract public long insert(int type, SQLiteDatabase db, ContentValues initialValues);

	abstract public boolean query(int type, Uri uri, SQLiteQueryBuilder queryBuilder, Context context, String[] projection);

	abstract public int update(int match, SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs);

	abstract public String[] init(boolean isUpgrade);

	abstract protected Class<? extends Dao> getDaoType();

	public final String create() throws IllegalArgumentException, IllegalAccessException {
		TableBuilder builder = new TableBuilder();
		Class<? extends Dao> cls = getDaoType();
		Field[] fields = cls.getDeclaredFields();
		for (Field field : fields) {
			Annotation[] annotations = field.getAnnotations();
			for (Annotation annotation : annotations) {
				if (annotation instanceof Column) {
					Column columnAnnotation = (Column) annotation;
					String columnName = columnAnnotation.name();
					switch (columnAnnotation.type()) {
						case Column.INTEGER:
						case Column.BOOLEAN:
						case Column.LONG:
						case Column.TIMESTAMP:
							builder.addInteger(columnName);
							break;
						case Column.DOUBLE:
							builder.addDouble(columnName);
							break;
						case Column.STRING:
							builder.addText(columnName);
							break;
					}
					break;
				}
			}
		}
		return builder.build();
	}

	abstract public String[] getProjection();

	protected final class TableBuilder {
		private StringBuilder mBuilder = new StringBuilder();

		public TableBuilder() {
			mBuilder.append("CREATE TABLE ").append(getTableName()).append(" (");
			mBuilder.append(BaseColumns._ID).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
		}

		public TableBuilder addDouble(String fieldName) {
			return addField(fieldName, "DOUBLE");
		}

		public TableBuilder addInteger(String fieldName) {
			return addField(fieldName, "INTEGER");
		}

		public TableBuilder addText(String fieldName) {
			return addField(fieldName, "TEXT");
		}

		private TableBuilder addField(String fieldName, String fieldType) {
			mBuilder.append(", ").append(fieldName).append(" ").append(fieldType);
			return this;
		}

		public String build() {
			mBuilder.append(");");
			return mBuilder.toString();
		}

		@Override
		public String toString() {
			return build();
		}
	}

	protected final class InsertBuilder {
		private StringBuilder mBuilder = new StringBuilder();
		private HashMap<String, String> mData = new HashMap<String, String>();

		public InsertBuilder() {
			mBuilder.append("INSERT INTO ").append(getTableName()).append(" (");
		}

		public InsertBuilder add(String field, String value) {
			mData.put(field, value);
			return this;
		}

		public InsertBuilder add(String field, long value) {
			return add(field, String.valueOf(value));
		}

		public String build() {
			Set<String> keySet = mData.keySet();
			final int length = keySet.size();
			String[] values = new String[length];

			int i = 0;
			for (String key : keySet) {
				values[i] = mData.get(key);
				mBuilder.append(key);
				if (i + 1 < length) {
					mBuilder.append(",");
				}
				i++;
			}
			mBuilder.append(") VALUES (");
			for (i = 0; i < length; i++) {
				mBuilder.append("'").append(values[i]).append("'");
				if (i + 1 < length) {
					mBuilder.append(",");
				}
			}

			mBuilder.append(");");
			return mBuilder.toString();
		}

		@Override
		public String toString() {
			return build();
		}
	}
}
